import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

"""
决策树分类算法是一种监督学习算法，它的基本原理是将数据集通过一系列的问题进行拆分，这些问题被视为决策树的叶子节点和内部节点。
决策树的每个分支代表一个可能的决策结果，而每个叶子节点代表一个最终的分类结果。

决策树分类算法的历史可以追溯到1980年代初，当时研究者开始探索用机器学习来解决分类问题。
在1981年，J.Ross Quinlan开发了ID3算法，该算法使用信息增益来选择决策树的最佳划分属性。
后来，在1986年，J.Ross Quinlan提出了C4.5算法，该算法引入了剪枝技术，以防止过拟合，该算法还引入了处理连续属性、缺失数据和多值属性等新特性。
在1998年，Jerome Friedman等人提出了CART算法（Classification and Regression Trees），该算法采用了二叉树，使得决策树更加简洁和易于解释。

1. 算法概述
决策树不仅可以用在分类问题上，也可以用在回归问题上。
关于决策树在回归问题上的应用，可以参考：TODO

回到决策树分类算法上来，构建决策树的有三种算法：

1.1. ID3
ID3算法的完整名称是Iterative Dichotomiser 3，即迭代二叉树3代。
ID3算法的核心思想是以信息增益来度量属性的选择，选择分裂后信息增益最大的属性进行分裂。

对于任意样本数据 
x(x1,x2,...,xn)，它的信息熵定义为：entropy(x)=−∑ni=1pilog2(pi)
基于信息熵，信息增益的公式为：IG(T)=entropy(S)−∑value(T)|Sx|/|S|entropy(Sx)
其中：S 表示全部样本的集合|S| 表示S中样本数量T 表示样本的某个特征
value(T) 表示特征T所有的取值集合Sx 是S中特征T的值为x的样本的集合|Sx| 表示Sx中样本数量
1.2. C4.5
C4.5算法是以ID3算法为基础的，它改为使用信息增益率来作为决策树分裂的依据。
这样，就克服了ID3算法中信息增益选择属性时偏向选择取值多的属性的不足。

C4.5算法中引入了一个分裂信息（split information）的项来惩罚取值较多的特征：
SI(T)=−∑value(T)|Sx|/|S|log|Sx|/|S|基于此，信息增益率的公式为：gainRatio(T)=IG(T)/SI(T)
IG(T)就是上一节ID3算法中的信息增益公式。

1.3. CART
CART算法全称是 classification and regression tree（分类与回归树）。
这个算法既可以用来分类，也可以用来回归，在回归问题上的介绍可以参考。

CART算法是根据基尼系数（Gini）来划分特征的，每次选择基尼系数最小的特征作为最优切分点。
其中基尼系数的计算方法：
gini(p)=∑ni=1pi(1−pi)=1−∑ni=1p2i
2. 创建样本数据
用scikit-learn中的样本生成器make_classification来生成分类用的样本数据。"""

import matplotlib.pyplot as plt
from sklearn.datasets import make_classification

# 分类数据的样本生成器
X, y = make_classification(n_samples=1000, n_classes=4, n_clusters_per_class=1, n_informative=6)
# plt.scatter(X[:, 0], X[:, 1], marker="o", c=y, s=25)
#
# plt.show()
"""image.png
关于样本生成器的详细内容，请参考：TODO

3. 模型训练
首先，分割训练集和测试集。"""


# 分割训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 这次按照8:2的比例来划分训练集和测试集。
#
# 然后用不同的算法来训练决策树模型：

reg_names = [
    "ID3算法",
    "C4.5算法",
    "CART算法",
]

# 定义
regs = [
    DecisionTreeClassifier(criterion="entropy"),
    DecisionTreeClassifier(criterion="log_loss"),
    DecisionTreeClassifier(criterion="gini"),
]

# 训练模型
for reg in regs:
    reg.fit(X_train, y_train)

# 在测试集上进行预测
y_preds = []
y_pred = None
for reg in regs:
    y_pred = reg.predict(X_test)
    y_preds.append(y_pred)

for i in range(len(y_preds)):
    correct_pred = np.sum(y_preds[i] == y_test)
    print("【{}】 预测正确率：{:.2f}%".format(reg_names[i], correct_pred / len(y_pred) * 100))

"""# 运行结果
【ID3算法】 预测正确率：71.50%
【C4.5算法】 预测正确率：72.50%
【CART算法】 预测正确率：75.00%
算法的正确率差别不是特别大。
感兴趣的朋友，可以尝试调整样本生成器部分，生成一些特征较多的数据来看看算法之间的性能差别。

4. 总结
决策树分类算法广泛应用于图像识别、文本分类、语音识别、信用评分、疾病诊断等众多领域。
例如，在电商平台上，可以通过决策树分类算法对用户的行为数据进行挖掘和分析，实现对用户的精准推荐；
在医疗领域，可以通过对医学数据的分析，辅助医生进行疾病诊断和治疗方案制定。

决策树分类算法的优势有：

易于理解和解释，直观地展示出分类的过程
对于数据集可以进行并行处理，提高了算法的效率
对于缺失数据和非数值属性有很好的处理能力
可以处理多分类问题
决策树分类算法也存在一些劣势：

可能存在过拟合，需要使用剪枝技术来控制
可能存在偏向性，需要使用加权投票来处理
对于连续属性和多值属性处理起来比较复杂，需要额外的处理方法
大规模数据集处理起来比较耗时，需要优化算法或者使用分布式计算等方法"""